//
//  NSObject+KVO.m
//  KVO实现机制
//
//  Created by MyMac on 15/8/28.
//  Copyright (c) 2015年 tongfang. All rights reserved.
//

#import "NSObject+KVO.h"
#import <objc/runtime.h>
#import <CoreFoundation/CoreFoundation.h>
#import <objc/message.h>
@interface NSObject()
@property(nonatomic,copy)CallBlock callBlock;
@property(nonatomic,copy)NSString*propertyName;
@end

@implementation NSObject (KVO)
/**
 *  动态的创建一个被观察对象的子类，将这个对象的类指定为新创建的子类，重写属性的set方法，当调用这个set方法时进行回调，回调完成后再调用这个对象的父类的set方法（也就是原来的set方法）给这个属性赋值（否则实际是没有给这个属性赋值，重写的set方法只是进行回调，没有给属性赋值）并且重写了class 方法，返回原类（给外界的假象，这个对象的类没有改变）
 *
 *  @param object 观察者
 *  @param key    观察的属性
 *  @param block  回调block
 */
-(void)addobserver:(NSObject*)object andKey:(NSString*)key andBlock:(CallBlock)block
{
    self.callBlock=block;
    self.propertyName=key;

    Class objcClass=[self class];
    const char*tempClassName=[@"newClass" cStringUsingEncoding:NSASCIIStringEncoding];
    //新类
    Class tempClass=objc_getClass(tempClassName);
    if(!tempClass){
    tempClass=objc_allocateClassPair(objcClass, tempClassName, 0);
    objc_registerClassPair(tempClass);
    }
    //set方法字符串
    NSString*setKey=[self setKey:key];
    //将对象指向新类
    object_setClass(self, tempClass);
    //set方法SEL
    SEL sel=NSSelectorFromString(setKey);
 //方法列表，，无法读取父类的方法
//    unsigned int outCount=0;
//   Method*methodList=class_copyMethodList(objcClass, &outCount);
//    for (NSInteger i=0; i<outCount; i++) {
//        Method classMetho=methodList[i];
//        SEL sector=method_getName(classMetho);
//    NSLog(@"%@",NSStringFromSelector(sector));
//    }
//    free(methodList);
    
    
    //重写set方法
    Method setM=class_getInstanceMethod(tempClass,sel);
   const char*encodeType=method_getTypeEncoding(setM);
  class_addMethod(tempClass,sel,(IMP)impBlock, encodeType);
    
    //重写class方法
    Method classM=class_getInstanceMethod(objcClass, @selector(class));
    const char*classtype=method_getTypeEncoding(classM);
    class_addMethod(objcClass, @selector(class), imp_implementationWithBlock((Class)^(id self,SEL _cmd){
    
        return class_getSuperclass(object_getClass(self));
    }), classtype);



}
#pragma -mark 回调
//oc的方法实际就是至少带有两个参数（id self,SEL _cmd）的c函数
static void impBlock(id self,SEL _cmd,id newValue){


    
   //c函数里面不能调用点语法
    [self receiverBlock:newValue];
    //给属性赋值
    [self setPropertyNewValue:newValue andSEL:_cmd];
    
}
#pragma -mark 回调
-(void)receiverBlock:(id)newValue
{
  
    self.callBlock(self.propertyName,newValue,[self valueForKey:self.propertyName]);

}
#pragma -mark 给属性赋值，，，调用了set方法，但没给属性复制，那么并没有什么意义
-(void)setPropertyNewValue:(id)newValue andSEL:(SEL)sel{

    Class superC=object_getClass(self);
    struct objc_super superClass = {
        .receiver = self,
        .super_class = class_getSuperclass(superC)
    };
   
////    objc_msgSend(self,sel,newValue);
//    void (*objc_msgSendSuperCasted)(void *, SEL, id) = (void *)objc_msgSendSuper;
//   
//    objc_msgSendSuperCasted(&superClass, sel, newValue);
    objc_msgSendSuper(&superClass, sel,newValue);
    
}
#pragma -mark 返回set方法字串
-(NSString*)setKey:(NSString*)key
{
    NSString*header=[[key substringToIndex:1] uppercaseString];
    NSMutableString*newKey=[key mutableCopy];
    [newKey replaceCharactersInRange:NSMakeRange(0, 1) withString:header];
    return [[NSString stringWithFormat:@"set%@:",newKey] copy];
    
}

#pragma -mark 创建属性
static NSString *propertyKey;
-(void)setPropertyName:(NSString *)propertyName
{

  objc_setAssociatedObject(self, &propertyKey,propertyName, OBJC_ASSOCIATION_COPY_NONATOMIC);



}
-(NSString*)propertyName
{

    return objc_getAssociatedObject(self, &propertyKey);

}
static  NSString *blockyKey;
-(void)setCallBlock:(CallBlock)callBlock
{

    objc_setAssociatedObject(self, &blockyKey, callBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);

}
-(CallBlock)callBlock
{
    return objc_getAssociatedObject(self, &blockyKey);

}
@end
